/*
 *   COPYRIGHT (C) 2023 Art AUTHORS(fxzcloud@gmail.com). ALL RIGHTS RESERVED.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

package com.art.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.StrPool;
import com.art.common.dataPermission.enums.DataScopeEnum;
import com.art.common.tenant.context.TenantContextHolder;
import com.art.common.tenant.util.TenantUtils;
import com.art.core.common.constant.GlobalStatusEnum;
import com.art.core.common.constant.RoleEnum;
import com.art.core.common.exception.ArtException;
import com.art.system.api.role.dto.RoleDTO;
import com.art.system.api.tenant.dto.TenantDTO;
import com.art.system.api.tenant.dto.TenantPageDTO;
import com.art.system.api.user.dto.SystemUserDTO;
import com.art.system.core.convert.TenantConvert;
import com.art.system.dao.dataobject.TenantDO;
import com.art.system.dao.dataobject.TenantPackageDO;
import com.art.system.manager.TenantManager;
import com.art.system.service.TenantPackageService;
import com.art.system.service.TenantService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 租户表
 *
 * @author fxz
 * @date 2022-10-01
 */
@Slf4j
@Service
public class TenantServiceImpl implements TenantService {

	@Resource
	private TenantPackageService tenantPackageService;

	@Resource
	private UserServiceImpl userService;

	@Resource
	private RoleServiceImpl roleService;

	@Resource
	private TenantManager tenantManager;

	/**
	 * 校验租户信息是否合法
	 * @param id 租户id
	 */
	@Override
	public void validTenant(Long id) {
		TenantDO tenantDO = tenantManager.getTenantById(id);

		if (Objects.isNull(tenantDO)) {
			throw new ArtException("租户信息不存在!");
		}
		if (tenantDO.getStatus().equals(GlobalStatusEnum.DISABLE.getValue())) {
			throw new ArtException(String.format("租户未开启:%s", tenantDO.getName()));
		}
		if (LocalDateTime.now().isAfter(tenantDO.getExpireTime())) {
			throw new ArtException("租户已经过期！");
		}
	}

	/**
	 * 获取所有租户id集合
	 * @return 所有租户id集合
	 */
	@Override
	public List<Long> getTenantIds() {
		return tenantManager.listTenant().stream().map(TenantDO::getId).collect(Collectors.toList());
	}

	/**
	 * 保存租户信息
	 * @param tenant 租户信息
	 */
	@Transactional(rollbackFor = Exception.class)
	@Override
	public Boolean addSysTenant(TenantDTO tenant) {
		// 检查套餐信息
		TenantPackageDO tenantPackageDO = tenantPackageService.validTenantPackage(tenant.getPackageId());

		// 保存租户信息
		Long tenantId = tenantManager.addTenant(tenant);

		TenantUtils.run(tenantId, () -> {
			// 根据套餐信息为新租户新建一个角色
			Long roleId = createRole(tenantPackageDO);

			// 为新租户创建一个默认账号
			Long userId = createUser(roleId, tenant);

			// 更新租户管理员id
			tenantManager.updateTenantAdmin(tenant.getId(), userId);
		});

		return Boolean.TRUE;
	}

	/**
	 * 根据角色id与租户信息创建一个默认账号
	 * @param roleId 角色id
	 * @param tenant 租户信息
	 * @return userId
	 */
	private Long createUser(Long roleId, TenantDTO tenant) {
		// 创建用户
		SystemUserDTO systemUserDO = new SystemUserDTO().setUsername(tenant.getUsername())
			.setPassword(tenant.getPassword())
			.setMobile(tenant.getTenantAdminMobile())
			.setUsername(tenant.getTenantAdminName())
			.setRoleId(String.valueOf(roleId));

		return userService.createUser(systemUserDO).getUserId();
	}

	/**
	 * 根据套餐生成一个角色信息
	 * @param tenantPackageDO 租户套餐
	 * @return 角色信息
	 */
	private Long createRole(TenantPackageDO tenantPackageDO) {
		// 生成租户管理员角色角色
		RoleDTO roleDTO = new RoleDTO().setRoleName(RoleEnum.TENANT_ADMIN.getDescription())
			.setCode(RoleEnum.TENANT_ADMIN.getType())
			.setRemark("系统生成租户管理员角色")
			.setMenuId(tenantPackageDO.getMenuIds())
			.setDataScope(DataScopeEnum.ALL.getScope());

		return roleService.addRole(roleDTO).getRoleId();
	}

	/**
	 * 根据name查询租户信息
	 */
	@Override
	public Long findTenantIdById(String name) {
		TenantDO tenantDO = tenantManager.getTenantByName(name);
		if (Objects.isNull(tenantDO)) {
			return null;
		}

		return tenantDO.getId();
	}

	/**
	 * 删除租户
	 */
	@Override
	public Boolean deleteSysTenant(Long id) {
		if (isSystemTenant(id)) {
			throw new ArtException("系统内置租户，不允许删除");
		}

		return tenantManager.deleteTenantById(id) > 0;
	}

	/**
	 * 更新指定租户的角色菜单信息
	 * @param id 租户id
	 * @param menus 菜单信息
	 */
	@Override
	public void updateTenantRoleMenu(Long id, List<String> menus) {
		TenantUtils.run(id, () -> {
			// 本租户下的所有角色
			List<RoleDTO> roleDTOList = roleService.getAllRole();

			roleDTOList.forEach(r -> {
				if (Objects.equals(r.getCode(), RoleEnum.TENANT_ADMIN.getType())) {
					// 租户管理员 则直接赋值新菜单
					r.setMenuId(String.join(StrPool.COMMA, menus));
				}
				else {
					// 非租户管理员 则原菜单和现菜单取交集
					r.setMenuId(String.join(StrPool.COMMA,
							CollUtil.intersectionDistinct(menus, Arrays.asList(r.getMenuId().split(StrPool.COMMA)))));
				}

				// 更新角色信息
				roleService.editRole(r);
			});
		});
	}

	/**
	 * 校验租户下账号数量
	 */
	@Override
	public void validCount() {
		long count = userService.count();
		Long tenantId = TenantContextHolder.getTenantId();
		TenantDO tenantDO = tenantManager.getTenantById(tenantId);

		if (Objects.isNull(tenantDO) || count > tenantDO.getAccountCount()) {
			throw new ArtException("租户账号数量超过额度！");
		}
	}

	/**
	 * 根据id查询租户信息
	 * @param id 租户id
	 * @return 租户信息
	 */
	@Override
	public TenantDTO findById(Long id) {
		return TenantConvert.INSTANCE.convert(tenantManager.getTenantById(id));
	}

	/**
	 * 校验租户是否是系统租户
	 * @param id 租户id 我们任务租户id为1时为系统内置租户 不允许删除
	 * @return 是否是系统租户
	 */
	private boolean isSystemTenant(Long id) {
		return Objects.equals(id, TenantDO.TENANT_ID_SYSTEM);
	}

	/**
	 * 修改租户信息
	 */
	@Override
	public Boolean updateSysTenant(TenantDTO tenantDTO) {
		return tenantManager.updateTenant(tenantDTO) > 0;
	}

	/**
	 * 分页查询租户信息
	 */
	@Override
	public IPage<TenantDTO> pageSysTenant(TenantPageDTO pageDTO) {
		return TenantConvert.INSTANCE.convert(tenantManager.pageTenant(pageDTO));
	}

	/**
	 * 获取全部
	 */
	@Override
	public List<TenantDTO> findAll() {
		return TenantConvert.INSTANCE.convert(tenantManager.listTenant());
	}

}
